package com.piece.core.framework.util.schedule;

import com.piece.core.framework.constant.ScheduleConstants;
import com.piece.core.framework.enums.StatusType;
import com.piece.core.framework.exception.ServerException;
import com.piece.core.framework.support.convert.Convert;
import com.piece.core.framework.util.string.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import java.text.ParseException;
import java.util.Date;
import java.util.List;

/**
 * 定时任务工具类
 */
@Slf4j
public class ScheduleUtil {

    private final static String JOB_NAME = "TASK_";

    /**
     * 获取触发器key
     */
    public static TriggerKey getTriggerKey(String jobId) {
        return TriggerKey.triggerKey(JOB_NAME + jobId);
    }

    /**
     * 获取jobKey
     */
    public static JobKey getJobKey(String jobId) {
        return JobKey.jobKey(JOB_NAME + jobId);
    }

    /**
     * 获取表达式触发器
     */
    public static CronTrigger getCronTrigger(Scheduler scheduler, String jobId) {
        try {
            return (CronTrigger) scheduler.getTrigger(getTriggerKey(jobId));
        } catch (SchedulerException e) {
            log.error("获取定时任务({})异常：{}", jobId, e.getMessage());
            throw new ServerException(e.getMessage());
        }
    }

    /**
     * 创建定时任务
     */
    public static Date createScheduleJob(Scheduler scheduler, ScheduleJob scheduleJob) {
        try {
            // 构建job信息
            JobDetail jobDetail = JobBuilder.newJob(ScheduleJobUtil.class).withIdentity(getJobKey(scheduleJob.getKey())).build();

            // 表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCron())
                    .withMisfireHandlingInstructionDoNothing();
            scheduleBuilder = handleCronScheduleMisfirePolicy(scheduleJob, scheduleBuilder);

            // 按新的cronExpression表达式构建一个新的trigger
            CronTrigger trigger = TriggerBuilder.newTrigger()
                    .withIdentity(getTriggerKey(scheduleJob.getKey()))
                    .withSchedule(scheduleBuilder).build();

            // 放入参数，运行时的方法可以获取
            jobDetail.getJobDataMap().put(ScheduleConstants.JOB_PARAM_KEY, scheduleJob);

            // 判断是否存在
            if (scheduler.checkExists(getJobKey(scheduleJob.getKey()))) {
                // 防止创建时存在数据问题 先移除，然后在执行创建操作
                scheduler.deleteJob(getJobKey(scheduleJob.getKey()));
            }

            Date date = scheduler.scheduleJob(jobDetail, trigger);
            // 暂停任务
            if (StatusType.PAUSE.getCode().equals(scheduleJob.getStatus())) {
                pauseJob(scheduler, scheduleJob.getName(), scheduleJob.getKey());
            }
            return date;
        } catch (SchedulerException e) {
            log.error("创建定时任务({}-{})异常：{}", scheduleJob.getName(), scheduleJob.getKey(), e.getMessage());
            throw new ServerException(e.getMessage());
        }
    }

    /**
     * 更新定时任务
     */
    public static Date updateScheduleJob(Scheduler scheduler, ScheduleJob scheduleJob) {
        try {
            TriggerKey triggerKey = getTriggerKey(scheduleJob.getKey());

            // 判断是否存在
            if (!scheduler.checkExists(getJobKey(scheduleJob.getKey()))) {
                // 创建新任务
                return createScheduleJob(scheduler, scheduleJob);
            }

            // 表达式调度构建器
            CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(scheduleJob.getCron())
                    .withMisfireHandlingInstructionDoNothing();

            CronTrigger trigger = getCronTrigger(scheduler, scheduleJob.getKey());

            // 按新的cronExpression表达式重新构建trigger
            trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();

            // 参数
            trigger.getJobDataMap().put(ScheduleConstants.JOB_PARAM_KEY, scheduleJob);

            Date date = scheduler.rescheduleJob(triggerKey, trigger);
            // 暂停任务
            if (StatusType.PAUSE.getCode().equals(scheduleJob.getStatus())) {
                pauseJob(scheduler, scheduleJob.getName(), scheduleJob.getKey());
            }
            return date;
        } catch (SchedulerException e) {
            log.error("更新定时任务({}-{})异常：{}", scheduleJob.getName(), scheduleJob.getKey(), e.getMessage());
            throw new ServerException(e.getMessage());
        }
    }

    /**
     * 立即执行任务
     */
    public static void run(Scheduler scheduler, ScheduleJob scheduleJob) {
        try {
            // 判断是否存在
            if (!scheduler.checkExists(getJobKey(scheduleJob.getKey()))) {
                scheduleJob.setStatus(StatusType.PAUSE.getCode());
                // 创建新任务
                createScheduleJob(scheduler, scheduleJob);
            }
            // 参数
            JobDataMap dataMap = new JobDataMap();
            dataMap.put(ScheduleConstants.JOB_PARAM_KEY, scheduleJob);
            scheduler.triggerJob(getJobKey(scheduleJob.getKey()), dataMap);
        } catch (SchedulerException e) {
            log.error("执行定时任务({}-{})异常：{}", scheduleJob.getName(), scheduleJob.getKey(), e.getMessage());
            throw new ServerException(e.getMessage());
        }
    }

    /**
     * 暂停任务
     */
    public static void pauseJob(Scheduler scheduler, String name, String key) {
        try {
            scheduler.pauseJob(getJobKey(key));
        } catch (SchedulerException e) {
            log.error("暂停定时任务({}-{})异常：{}", name, key, e.getMessage());
            throw new ServerException(e.getMessage());
        }
    }

    /**
     * 恢复任务
     */
    public static void resumeJob(Scheduler scheduler, String name, String key) {
        try {
            scheduler.resumeJob(getJobKey(key));
        } catch (SchedulerException e) {
            log.error("恢复定时任务({}-{})异常：{}", name, key, e.getMessage());
            throw new ServerException(e.getMessage());
        }
    }

    /**
     * 删除定时任务
     */
    public static void deleteJob(Scheduler scheduler, String name, String key) {
        try {
            scheduler.deleteJob(getJobKey(key));
        } catch (SchedulerException e) {
            log.error("删除定时任务({}-{})异常：{}", name, key, e.getMessage());
            throw new ServerException(e.getMessage());
        }
    }

    /**
     * 启动所有定时任务
     */
    public void startAllJobs(Scheduler scheduler) {
        try {
            scheduler.start();
        } catch (Exception e) {
            log.error("启动全部定时任务异常：{}", e.getMessage());
            throw new ServerException(e.getMessage());
        }
    }

    /**
     * 关闭所有定时任务
     */
    public void shutdownAllJobs(Scheduler scheduler) {
        try {
            List<String> groupJobs = scheduler.getJobGroupNames();
            for (String jobName : groupJobs) {
                scheduler.deleteJob(JobKey.jobKey(jobName, jobName));
            }
            if (!scheduler.isInStandbyMode()) {
                scheduler.standby();
            }
        } catch (Exception e) {
            log.error("关闭全部定时任务异常：{}", e.getMessage());
            throw new ServerException(e.getMessage());
        }
    }

    public static CronScheduleBuilder handleCronScheduleMisfirePolicy(ScheduleJob job, CronScheduleBuilder cb) throws RuntimeException {
        switch (job.getMisfirePolicy()) {
            case ScheduleConstants.MISFIRE_DEFAULT:
                return cb;
            case ScheduleConstants.MISFIRE_IGNORE_MISFIRES:
                return cb.withMisfireHandlingInstructionIgnoreMisfires();
            case ScheduleConstants.MISFIRE_FIRE_AND_PROCEED:
                return cb.withMisfireHandlingInstructionFireAndProceed();
            default:
                return cb.withMisfireHandlingInstructionDoNothing();
        }
    }

    /**
     * 生成Cron表达式
     *
     * @param time 间隔时间，格式 yy-MM-dd HH:mm:ss
     * @return
     */
    public static String transCron(String time) {
        String seconds = StringUtil.substringAfterLast(time, ":");
        seconds = StringUtil.isEmpty(seconds) ? "*" : "/" + seconds;
        String minute = StringUtil.substringAfter(time, ":").substring(0, 2);
        minute = StringUtil.isEmpty(minute) ? "*" : "/" + Convert.toInt(minute);
        String hour = StringUtil.substringAfter(time, " ").substring(0, 2);
        hour = StringUtil.isEmpty(hour) ? "*" : "/" + Convert.toInt(hour);
        String day = StringUtil.substringAfterLast(time, "-").substring(0, 2);
        day = StringUtil.isEmpty(day) ? "*" : "/" + Convert.toInt(day);
        String month = StringUtil.substringAfter(time, "-").substring(0, 2);
        month = StringUtil.isEmpty(month) ? "*" : "/" + Convert.toInt(month);
        return seconds + " " + minute + " " + hour + " " + day + " " + month + " ?";
    }

    /**
     * 判断Cron表达式的有效性
     */
    public static boolean isValid(String cronExpression) {
        return CronExpression.isValidExpression(cronExpression);
    }

    /**
     * 判断Cron表达式的有效性
     *
     * @param cronExpression Cron表达式
     * @return S有效返回null, 无效返回表达式错误描述
     */
    public static String getInvalidMessage(String cronExpression) {
        try {
            new CronExpression(cronExpression);
            return null;
        } catch (ParseException pe) {
            return pe.getMessage();
        }
    }

    /**
     * 返回下次执行时间
     *
     * @param cronExpression Cron表达式
     */
    public static Date getNextExecution(String cronExpression) {
        try {
            CronExpression cron = new CronExpression(cronExpression);
            return cron.getNextValidTimeAfter(new Date(System.currentTimeMillis()));
        } catch (ParseException e) {
            throw new IllegalArgumentException(e);
        }
    }
}
